// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "filehdr.h"
#include "debug.h"
#include "synchdisk.h"
#include "main.h"

FileHeader::FileHeader( int initLevel/* = 0 */)
:level(initLevel)
,numBytes(0)
,numSectors(0)
{
}


//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------

bool
FileHeader::Allocate(PersistentBitmap *freeMap, int fileSize)
{ 
    if( level == 0 ) { 

        numBytes = fileSize;
        numSectors  = divRoundUp(fileSize, SectorSize);
        DEBUG( dbgZwj, "Allocate: level = " << level
                    << ", fileSize = " << fileSize << ", Sector = " << numSectors );

        if (freeMap->NumClear() < numSectors)
    	    return FALSE;		// not enough space

        for (int i = 0; i < numSectors && i < NumDirect; i++) {
            dataSectors[i] = freeMap->FindAndSet();
            DEBUG( dbgZwj, "Allocate: sector[" << i << "] = " << dataSectors[i] );
            ASSERT(dataSectors[i] >= 0);
        }
        
        if( numSectors <= NumDirect ) {
            dataSectors[NumDirect] = 0;
            DEBUG( dbgZwj, "Allocate: sector[" << NumDirect << "] = " << 0 );
            return TRUE;
        }
        else {
            dataSectors[NumDirect] = freeMap->FindAndSet();
            DEBUG( dbgZwj, "Allocate: sector[" << NumDirect << "] = " << dataSectors[NumDirect] );
        }

        FileHeader * hdr = new FileHeader(1);
        hdr->Allocate(freeMap, fileSize - SectorSize*NumDirect );
        hdr->WriteBack(dataSectors[NumDirect]);
        delete hdr;
    }
    else if( level == 1 ) {
        numBytes = fileSize;
        numSectors  = divRoundUp( fileSize, (NumTable*SectorSize));
        DEBUG( dbgZwj, "Allocate: level = " << level
                    << ", fileSize = " << fileSize << ", Sector = " << numSectors );

        int numBytesLeft = numBytes;
        int numOneSector = 0;
        
        for( int i = 0; i < numSectors && i < NumTable; i++ ) {
            if( numBytesLeft > NumTable * SectorSize ) {
                numOneSector = NumTable * SectorSize;
            }
            else {
                numOneSector = numBytesLeft;
            }
            numBytesLeft -= numOneSector; 
            DEBUG( dbgZwj, "Allocate: numOneSector = " << numOneSector
                        << ", numBytesLeft = " << numBytesLeft );
            
            dataSectors[i] = freeMap->FindAndSet();
            DEBUG( dbgZwj, "Allocate: sector[" << i << "] = " << dataSectors[i] );
            ASSERT(dataSectors[i] >= 0);

            FileHeader * hdr = new FileHeader(2);
            hdr->Allocate(freeMap, numOneSector);
            hdr->WriteBack(dataSectors[i]);
            delete hdr;
        }
    }
    else if( level == 2 ) {
        numBytes = fileSize;
        numSectors  = divRoundUp(fileSize, SectorSize);
        DEBUG( dbgZwj, "Allocate: level = " << level
                    << ", fileSize = " << fileSize << ", Sector = " << numSectors );

        for (int i = 0; i < numSectors && i < NumTable; i++) {
            dataSectors[i] = freeMap->FindAndSet();
            DEBUG( dbgZwj, "Allocate: sector[" << i << "] = " << dataSectors[i] );
            ASSERT(dataSectors[i] >= 0);
        }
    }
    
    return TRUE;
}

//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void 
FileHeader::Deallocate(PersistentBitmap *freeMap)
{
    for (int i = 0; i < numSectors; i++) {
	ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
	freeMap->Clear((int) dataSectors[i]);
    }
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void
FileHeader::FetchFrom(int sector)
{
    kernel->synchDisk->ReadSector(sector, (char *)this);
}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void
FileHeader::WriteBack(int sector)
{
    kernel->synchDisk->WriteSector(sector, (char *)this); 
}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int
FileHeader::ByteToSector(int offset)
{
    DEBUG( dbgZwj, "ByteToSector: offset =" << offset 
                    << ", level = " << level ); 
 
    if( offset < NumDirect * SectorSize  || level == 2 ){
        DEBUG( dbgZwj, "ByteToSector: offset =" << offset 
                    << ", level = " << level 
                    << ", sector = " << dataSectors[offset / SectorSize] );
        return(dataSectors[offset / SectorSize]);
    }

    if ( level == 0) {
    FileHeader * jumpHdr = new FileHeader(1);
    jumpHdr->FetchFrom(dataSectors[NumDirect]);
    
    offset -= NumDirect * SectorSize;
    int goalHdrIndex = offset / (SectorSize*NumTable);
    int goalHdrSector = jumpHdr->dataSectors[goalHdrIndex];
    
    DEBUG( dbgZwj, "ByteToSector: offset = " << offset
                << "goalHdrIndex = " << goalHdrIndex
                << "goalHdrSector = " << goalHdrSector );
    FileHeader * goalHdr = new FileHeader(2);
    goalHdr->FetchFrom(goalHdrSector);
    while( offset >= SectorSize*NumTable ) offset -= SectorSize*NumTable;
    int sector = goalHdr->ByteToSector(offset);
    
    delete jumpHdr;
    delete goalHdr;
    return sector; 
    }
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
FileHeader::FileLength()
{
    return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.
//----------------------------------------------------------------------

void
FileHeader::Print()
{
    int i, j, k;
    char *data = new char[SectorSize];

    printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
    for (i = 0; i < numSectors; i++)
	printf("%d ", dataSectors[i]);
    printf("\nFile contents:\n");
    for (i = k = 0; i < numSectors; i++) {
	kernel->synchDisk->ReadSector(dataSectors[i], data);
        for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
	    if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
		printf("%c", data[j]);
            else
		printf("\\%x", (unsigned char)data[j]);
	}
        printf("\n"); 
    }
    delete [] data;
}

//	changer the file header
//----------------------------------------------------------------------------
bool
FileHeader::AppendFileHdr(FileHeader* S_FileHdr,  PersistentBitmap *freeMap, int numSectorsTmp)
{
	int i = 0;
	this->numBytes += S_FileHdr->numBytes;
	this->numSectors = divRoundUp(this->numBytes, SectorSize);
	for (i = numSectorsTmp;i < this->numSectors;i++)
	{
		dataSectors[i] = freeMap->FindAndSet();
		ASSERT(dataSectors[i] >= 0);
	}
}
